//
// StandardNubbinKnobParams
//
// Copyright (C) jextra.net.
//
//  This file is part of the net.jextra.crackdep Library.
//
//  The net.jextra.crackdep Library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  The net.jextra.crackdep Library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with the net.jextra.crackdep Library; if not, write to the Free
//  Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
//  02111-1307 USA.
//

package net.jextra.crackdep;

import java.io.*;
import java.util.*;
import net.jextra.crack.*;
import net.jextra.crack.java.*;
import net.jextra.crack.props.*;
import net.jextra.crack.tool.*;
import net.jextra.crack.type.*;
import net.jextra.dep.*;
import net.jextra.dep.nubbin.*;
import net.jextra.dep.repo.*;

public class StandardNubbinKnobParams implements JavaKnobParams, NubbinKnobParams
{
    // ============================================================
    // Fields
    // ============================================================

    public static final String STYLE = "nubbin";
    public static final String SUFFIX_JAR = ".jar";

    public static final String DIR_ENV = "env";
    public static final String DIR_TARGET = "target";
    public static final String DIR_CLASSES = "classes";
    public static final String DIR_JAVADOC = "javadoc";
    public static final String DIR_BUILD = "build";
    public static final String DIR_SHRINKWRAP = "shrinkwrap";

    private File projectRootDir;
    private File selfNubbinFile;
    private String componentName;
    private String buildName;
    private String versionNumber;
    private DepDirFilter dirFilter;
    private DepConfReader confReader;
    private DepTree depTree;
    private DepTree teamDepModel;
    private HashSet<File> sourceDirs;
    private FileSet sourceFileSet;
    private File targetDir;
    private File envDir;
    private File classesDir;
    private File javadocDir;
    private File rootBuildDir;
    private File rootShrinkwrapDir;

    // ============================================================
    // Constructors
    // ============================================================

    public StandardNubbinKnobParams()
    {
        //
        // Search for nubbin.xml file in order to find component root.
        //
        for ( File dir = new File( NubbinReader.NUBBIN_FILENAME ).getAbsoluteFile().getParentFile(); dir != null; dir = dir
            .getParentFile() )
        {
            File file = new File( dir, NubbinReader.NUBBIN_FILENAME );
            if ( file.exists() && file.isFile() )
            {
                selfNubbinFile = file;
                break;
            }
        }

        assert selfNubbinFile != null : "Unable to find " + NubbinReader.NUBBIN_FILENAME + " file in source path.";

        //
        // WARNING: It is assumed that the project's root dir is always just the parent of the component root dir.
        //
        projectRootDir = getComponentRootDir().getParentFile();

        dirFilter = new StandardDirFilter( this );

        //
        // Parse nubbin.xml file.
        //
        confReader = new DepConfReader( STYLE );
        try
        {
            FileInputStream in = new FileInputStream( CrackBuild.getInstance().getCrackContext()
                .locateConfigFile( DepConfReader.FILENAME ) );
            confReader.parse( in );
            in.close();

            ArrayList<DepRepo> repos = new ArrayList<DepRepo>();
            repos.add( confReader.getLocalRepo() );

            NubbinReader reader = new NubbinReader();
            DepComponent component = reader.read( selfNubbinFile.toURI().toURL() );

            componentName = component.getName();
            int slash = componentName.lastIndexOf( '/' );
            buildName = componentName.substring( slash + 1 );

            versionNumber = component.getVersionNumber();
            assert versionNumber != null : "Version number must be specified in nubbin.xml file.";

            //
            // Search for source dirs and fileset.
            //
            RegexFileStrainer strainer = new RegexFileStrainer().include( ".*\\.java" );
            sourceFileSet = new StrainedFileSet( getComponentRootDir(), strainer );

            //
            // Find all paths to .java files in component.
            //
            sourceDirs = new LinkedHashSet<File>();
            JavaPackageFinder finder = new JavaPackageFinder();
            for ( File javaFile : strainer.getFiles( getComponentRootDir() ) )
            {
                finder.read( javaFile );
                sourceDirs.add( finder.getRootDir() );
            }
        }
        catch ( Exception ex )
        {
            throw new RuntimeException( ex );
        }

        //
        // Calculate output dirs.
        //
        envDir = new File( projectRootDir, DIR_ENV );
        targetDir = new File( projectRootDir, DIR_TARGET );
        javadocDir = new File( targetDir, DIR_JAVADOC );
        rootBuildDir = new File( targetDir, DIR_BUILD );
        rootShrinkwrapDir = new File( targetDir, DIR_SHRINKWRAP );

        //
        // Override classesDir if specified in Props by now.
        //
        if ( CrackProps.getString( "classes.dir" ) != null )
        {
            classesDir = new File( getComponentRootDir(), CrackProps.getString( "classes.dir" ) );
        }
        else
        {
            classesDir = new File( targetDir, DIR_CLASSES );
        }
    }

    // ============================================================
    // Methods
    // ============================================================

    @Override
    public String getBuildName()
    {
        return buildName;
    }

    @Override
    public String getComponentName()
    {
        return componentName;
    }

    public File getProjectRootDir()
        throws Exception
    {
        return projectRootDir;
    }

    @Override
    public File getComponentRootDir()
    {
        return selfNubbinFile.getParentFile();
    }

    @Override
    public File getSelfNubbinFile()
        throws Exception
    {
        return selfNubbinFile;
    }

    @Override
    public Collection<File> getSourceDirs()
    {
        return sourceDirs;
    }

    @Override
    public FileSet getSourceFileSet()
    {
        return sourceFileSet;
    }

    public File getEnvDir()
    {
        return envDir;
    }

    public File getTargetDir()
    {
        return targetDir;
    }

    @Override
    public File getClassesDir()
    {
        return classesDir;
    }

    @Override
    public File getJavadocDir()
    {
        return javadocDir;
    }

    @Override
    public File getBuildDir()
    {
        File dir = new File( rootBuildDir, getBuildName() );
        dir = new File( dir, getVersionNumber() );

        return dir;
    }

    @Override
    public File getShrinkwrapDir()
        throws Exception
    {
        File dir = new File( rootShrinkwrapDir, getBuildName() );
        dir = new File( dir, getVersionNumber() );

        return dir;
    }

    @Override
    public String getVersionNumber()
    {
        return versionNumber;
    }

    @Override
    public Collection<File> getResourceDirs()
    {
        return getSourceDirs();
    }

    @Override
    public String getClasspath()
        throws Exception
    {
        try
        {
            StringBuilder builder = new StringBuilder();
            for ( File file : getDependencies( "compile", "runtime" ) )
            {
                if ( builder.length() > 0 )
                {
                    builder.append( File.pathSeparator );
                }
                builder.append( file.getCanonicalPath() );
            }

            return builder.toString();
        }
        catch ( Exception ex )
        {
            throw new DepException( ex );
        }
    }

    @Override
    public FileRepo getLocalRepo()
        throws Exception
    {
        return (FileRepo) confReader.getLocalRepo();
    }

    @Override
    public DepTree getDepTree()
    {
        assert confReader.getLocalRepo() != null : "getLocalRepo can not be null.";

        try
        {
            if ( depTree == null )
            {
                ArrayList<DepRepo> repos = new ArrayList<DepRepo>();
                repos.add( confReader.getLocalRepo() );

                NubbinExplorer explorer = new NubbinExplorer();
                depTree = explorer.execute( projectRootDir, repos, dirFilter );
            }

            return depTree;
        }
        catch ( Exception ex )
        {
            throw new DepException( ex );
        }
    }

    @Override
    public DepTree getTeamDepTree()
        throws Exception
    {
        assert confReader.getTeamRepos() != null : "getTeamRepos can not be null.";

        try
        {
            if ( teamDepModel == null )
            {
                NubbinExplorer explorer = new NubbinExplorer();
                teamDepModel = explorer.execute( projectRootDir, confReader.getTeamRepos(), dirFilter );
            }

            return teamDepModel;
        }
        catch ( Exception ex )
        {
            throw new DepException( ex );
        }
    }

    @Override
    public DepRepo getTeamRepo( String name )
        throws Exception
    {
        return confReader.getTeamRepo( name );
    }

    @Override
    public Collection<File> getDependencies( String ... scopes )
    {
        try
        {
            LinkedHashSet<File> list = new LinkedHashSet<File>();

            DepTreeSearcher searcher = new DepTreeSearcher( getDepTree() );
            for ( DepComponentFile entry : searcher.getFiles() )
            {
                File file = getLocalRepo().getFile( entry.getComponent().getName(),
                    entry.getComponent().getVersionNumber(), entry.getName() );
                list.add( file );
            }

            return list;
        }
        catch ( Exception ex )
        {
            throw new DepException( ex );
        }
    }

    // ============================================================
    // Inner Classes
    // ============================================================

    public static class StandardDirFilter implements DepDirFilter
    {
        private StandardNubbinKnobParams params;

        public StandardDirFilter( StandardNubbinKnobParams params )
        {
            this.params = params;
        }

        @Override
        public boolean isDepSearchDir( File dir )
            throws Exception
        {
            if ( dir.getName().startsWith( "." ) )
            {
                return false;
            }
            else if ( isSameDir( dir, params.getEnvDir() ) )
            {
                return false;
            }
            else if ( isSameDir( dir, params.getTargetDir() ) )
            {
                return false;
            }
            else if ( isSameDir( dir, params.getClassesDir() ) )
            {
                return false;
            }
            else if ( isSameDir( dir, params.getJavadocDir() ) )
            {
                return false;
            }
            else if ( isSameDir( dir, params.getBuildDir() ) )
            {
                return false;
            }
            else if ( isSameDir( dir, params.getShrinkwrapDir() ) )
            {
                return false;
            }

            return true;
        }

        public boolean isSameDir( File dir1, File dir2 )
            throws IOException
        {
            return dir1.getCanonicalPath().equalsIgnoreCase( dir2.getCanonicalPath() );
        }
    }
}
